use std::sync::Arc;

use anyhow::{Context, Result};
use clap::Parser;
use iroh::SecretKey;
use n0_future::StreamExt;
use shared::{ChatNode, ChatTicket, Event};
use tokio::sync::RwLock;

mod sleep_inhibitor;
mod terminal;
mod terminal_manager;
mod web_proxy;
use sleep_inhibitor::SleepInhibitor;
use terminal_manager::TerminalManager;
use web_proxy::WebProxyManager;

#[derive(Parser)]
#[command(name = "iroh-chat-term")]
#[command(about = "Terminal chat and sharing application")]
struct Cli {
    #[arg(long, help = "Prevent system from sleeping while running")]
    no_sleep: bool,
}

#[tokio::main]
async fn main() -> Result<()> {
    let cli = Cli::parse();
    tracing_subscriber::fmt::init();

    let secret_key = match std::env::var("IROH_SECRET") {
        Err(_) => {
            let secret_key = {
                let bytes = rand::random::<[u8; 32]>();
                SecretKey::from_bytes(&bytes)
            };
            println!("* using new secret. to reuse, set this environment variable:");
            println!(
                "IROH_SECRET={}",
                data_encoding::HEXLOWER.encode(&secret_key.to_bytes())
            );
            secret_key
        }
        Ok(s) => s
            .parse()
            .context("failed to parse secret key from IROH_SECRET environment variable")?,
    };

    let node = ChatNode::spawn(Some(secret_key)).await?;

    // Initialize sleep inhibitor if --no-sleep flag is provided
    let sleep_inhibitor = if cli.no_sleep {
        println!("* System sleep inhibition enabled");
        match SleepInhibitor::new().await {
            Ok(inhibitor) => {
                println!("* Successfully inhibited system sleep");
                Some(inhibitor)
            }
            Err(e) => {
                eprintln!("* Warning: Failed to inhibit system sleep: {}", e);
                None
            }
        }
    } else {
        None
    };

    // Run terminal mode directly
    let result = run_terminal_mode(node).await;

    // Cleanup sleep inhibitor
    if let Some(inhibitor) = sleep_inhibitor {
        if let Err(e) = inhibitor.release().await {
            eprintln!("* Warning: Failed to release sleep inhibition: {}", e);
        } else {
            println!("* System sleep inhibition released");
        }
    }

    result
}

async fn run_terminal_mode(node: ChatNode) -> Result<()> {
    let ticket = ChatTicket::new_random();
    let mut our_ticket = ticket.clone();
    our_ticket.bootstrap = [node.node_id()].into_iter().collect();
    println!("* node id: {}", node.node_id());
    println!("* ticket to join this terminal session:");
    println!("{}", our_ticket.serialize());

    let terminal_manager = Arc::new(RwLock::new(TerminalManager::new(node.node_id())));
    let manager_clone = terminal_manager.clone();

    // Create web proxy manager
    let web_proxy_manager = Arc::new(RwLock::new(WebProxyManager::new(Arc::new(
        node.endpoint().clone(),
    ))));
    let web_manager_clone = web_proxy_manager.clone();

    println!("* waiting for terminal connections ...");
    let (sender, mut receiver) = node.join(&ticket, "terminal-server".to_string()).await?;

    let receive_task = tokio::task::spawn(async move {
        while let Some(event) = receiver.try_next().await? {
            match event {
                Event::NodeJoined { node_id } => {
                    println!("* neighbor up: {node_id}");

                    // Use optimized state synchronization
                    println!("* broadcasting terminal state to new neighbor");
                    let manager = manager_clone.read().await;
                    if let Err(e) = manager.sync_state_optimized(sender.clone()).await {
                        println!("* failed to broadcast terminal state: {}", e);
                    } else {
                        println!("* broadcasted terminal state to new neighbor");
                    }

                    // Broadcast WebShare state to new neighbor
                    println!("* broadcasting WebShare state to new neighbor");
                    let web_manager = web_manager_clone.read().await;
                    let web_share_proxies = web_manager.get_active_proxies();
                    if !web_share_proxies.is_empty() {
                        if let Err(e) = sender.send_web_share_state(web_share_proxies).await {
                            println!("* failed to broadcast WebShare state: {}", e);
                        } else {
                            println!("* broadcasted WebShare state to new neighbor");
                        }
                    }
                }
                Event::NodeLeft { node_id } => {
                    println!("* neighbor down: {node_id}")
                }
                Event::MessageReceived {
                    from,
                    text,
                    nickname,
                } => {
                    let from_short = from.fmt_short();
                    if text.is_empty() {
                        // Presence message
                        println!("* {from_short} is now known as {nickname}");
                    } else {
                        // Regular chat message
                        println!("<{from_short}> {nickname}: {text}");
                    }
                }
                Event::TerminalBatch {
                    from: _,
                    operations,
                } => {
                    // Optimized batch processing of terminal operations
                    for operation in operations {
                        match operation {
                            shared::TerminalOp::Create {
                                session_id,
                                shell_path: _,
                                name,
                            } => {
                                println!("* terminal create request for session {}", session_id);
                                let mut manager = manager_clone.write().await;
                                if let Err(e) = manager
                                    .create_session(
                                        session_id.clone(),
                                        name.clone(), // name
                                        None,         // Use default shell
                                        sender.clone(),
                                    )
                                    .await
                                {
                                    println!(
                                        "* failed to create terminal session {}: {}",
                                        session_id, e
                                    );
                                }
                            }
                            shared::TerminalOp::Input { session_id, data } => {
                                let manager = manager_clone.read().await;
                                if let Err(e) = manager.send_input(&session_id, data).await {
                                    println!(
                                        "* failed to forward input to terminal {}: {}",
                                        session_id, e
                                    );
                                }
                            }
                            shared::TerminalOp::Output { session_id, data } => {
                                if let Ok(text) = String::from_utf8(data.clone()) {
                                    println!("[Terminal {}] {}", session_id, text);
                                } else {
                                    println!("[Terminal {}] (binary data)", session_id);
                                }
                            }
                            shared::TerminalOp::Resize {
                                session_id,
                                rows,
                                cols,
                            } => {
                                let manager = manager_clone.read().await;
                                if let Err(e) = manager
                                    .resize_terminal(&session_id, rows as u16, cols as u16)
                                    .await
                                {
                                    println!(
                                        "* terminal {} failed to resize to {}x{}: {}",
                                        session_id, rows, cols, e
                                    );
                                }
                            }
                            shared::TerminalOp::Status { session_id, status } => {
                                println!(
                                    "* terminal status for session {} - {}",
                                    session_id, status
                                );
                            }
                            shared::TerminalOp::Close { session_id } => {
                                let mut manager = manager_clone.write().await;
                                if let Err(e) = manager.close_session(&session_id).await {
                                    println!(
                                        "* failed to close terminal session {}: {}",
                                        session_id, e
                                    );
                                }
                            }
                            shared::TerminalOp::Rename {
                                session_id,
                                new_name,
                            } => {
                                let display_name = new_name.as_deref().unwrap_or("no name");
                                println!("* terminal {} renamed to '{}'", session_id, display_name);

                                let mut manager = manager_clone.write().await;
                                if let Err(e) = manager.rename_session(&session_id, new_name).await
                                {
                                    println!("* failed to rename terminal {}: {}", session_id, e);
                                }
                            }
                        }
                    }
                }
                Event::TerminalState { from: _, terminals } => {
                    println!(
                        "* terminal state received with {} terminals",
                        terminals.len()
                    );

                    // Optimized state synchronization - batch create missing terminals
                    let mut manager = manager_clone.write().await;
                    let existing_sessions = manager.list_sessions();

                    for terminal_info in terminals {
                        if !existing_sessions.contains(&terminal_info.session_id) {
                            println!(
                                "* creating terminal {} from state sync",
                                terminal_info.session_id
                            );
                            if let Err(e) = manager
                                .create_session(
                                    terminal_info.session_id.clone(),
                                    terminal_info.name.clone(),
                                    None,
                                    sender.clone(),
                                )
                                .await
                            {
                                println!(
                                    "* failed to create terminal {}: {}",
                                    terminal_info.session_id, e
                                );
                            }
                        }
                    }
                }
                Event::WebShare {
                    from,
                    local_port,
                    public_port,
                } => {
                    println!(
                        "* web share request from {}: {} -> {}",
                        from.fmt_short(),
                        public_port,
                        local_port
                    );

                    let mut manager = web_manager_clone.write().await;
                    if let Err(e) = manager
                        .start_proxy(local_port, public_port, from.into(), None)
                        .await
                    {
                        println!("* failed to start web proxy: {}", e);
                    } else {
                        println!("* web proxy started successfully");
                    }
                }
                Event::WebShareStop { from, public_port } => {
                    println!(
                        "* web share stop request from {}: port {}",
                        from.fmt_short(),
                        public_port
                    );

                    let mut manager = web_manager_clone.write().await;
                    if let Err(e) = manager.stop_proxy(public_port).await {
                        println!("* failed to stop web proxy: {}", e);
                    } else {
                        println!("* web proxy stopped successfully");
                    }
                }
                Event::WebShareState { from: _, proxies } => {
                    println!(
                        "* web share state received with {} active proxies",
                        proxies.len()
                    );

                    // Process incoming WebShare state - recreate proxies locally
                    {
                        let _manager = web_manager_clone.write().await;
                        let local_node_id = node.node_id();

                        for proxy_info in proxies {
                            // Only create proxies that belong to other nodes
                            if proxy_info.node_id != local_node_id {
                                // For now, we just log the incoming proxy info
                                // In a full implementation, you might want to create local
                                // forwarding rules or track these in a UI
                                println!(
                                    "* remote proxy available: {} -> {} from node {}",
                                    proxy_info.public_port,
                                    proxy_info.local_port,
                                    proxy_info.node_id.fmt_short()
                                );
                            }
                        }
                    }
                }
                Event::Lagged => {
                    println!("* warn: gossip stream lagged")
                }
            }
        }
        anyhow::Ok(())
    });

    // Handle terminal resize signals
    let resize_task = tokio::task::spawn(async move {
        // Here you could handle terminal resize signals if needed
        // For now, we'll just keep the task alive
        tokio::time::sleep(tokio::time::Duration::from_secs(u64::MAX)).await;
        anyhow::Ok(())
    });

    // Wait for either task to complete
    tokio::select! {
        result = receive_task => {
            result??;
        }
        result = resize_task => {
            result??;
        }
    }

    // Cleanup
    let mut manager = terminal_manager.write().await;
    manager.close_all_sessions().await?;

    // Cleanup web proxies
    let mut web_manager = web_proxy_manager.write().await;
    web_manager.stop_all_proxies().await?;

    println!("* terminal server shutdown");
    Ok(())
}
